﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using System.Timers;

namespace CacheSharp.Core
{
    /// <summary>
    /// 缓存器工厂类。应调用此类建立缓存器
    /// Cacher factory class. Any cacher should be created here.
    /// </summary>
    public class CacheFactory : ICacher, ICleaner
    {
        #region  Variables and Properties

        /// <summary>
        /// 存储当前已有的存储器类型
        /// </summary>
        private static Dictionary<string, Type> _dicCacherNames;

        /// <summary>
        /// 当前使用的存储器
        /// </summary>
        private static ICacher _cacher;

        /// <summary>
        /// 默认的清理间隔时间（单位：ms）
        /// </summary>
        private const int DefaultInterval = 10000;

        /// <summary>
        /// 最短的清理间隔时间（单位：ms）
        /// </summary>
        private const int LeastInterval = 500;

        /// <summary>
        /// 当前清理时间
        /// </summary>
        private static int _interval;

        /// <summary>
        /// 清理定时器
        /// </summary>
        private static readonly Timer TimerClean;

        #endregion

        #region Constructors

        static CacheFactory()
        {
            _interval = DefaultInterval;
            IntiCacherNams();
            TimerClean = new Timer(_interval);
            TimerClean.Elapsed += TimerCleanElapsed;
        }

        /// <summary>
        /// 计时器定时清理过期数据
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private static void TimerCleanElapsed(object sender, ElapsedEventArgs e)
        {
            if (_cacher == null) return;

            _cacher.TryTrimAll();
        }

        /// <summary>
        /// 初始化存储器类型列表
        /// </summary>
        private static void IntiCacherNams()
        {
            if (_dicCacherNames != null) return;
            _dicCacherNames = new Dictionary<string, Type>();

            Assembly a = Assembly.GetExecutingAssembly();
            Type[] types = a.GetTypes();
            Type tICacher = typeof(ICacher);
            foreach (Type t in types)
            {
                if (tICacher.IsAssignableFrom(t) && !t.Name.Contains("CacheFactory") && !t.Name.Equals("ICacher"))
                {
                    try
                    {
                        ICacher c = Activator.CreateInstance(t) as ICacher;
                        if (c != null)
                        {
                            _dicCacherNames.Add(string.IsNullOrEmpty(c.CacherName) ? t.Name : c.CacherName, t);
                        }
                    }
                    catch (TypeInitializationException) {}
                }
            }
        }

        /// <summary>
        /// 得到可用的存储器名称集合
        /// Get cacher names can be used.
        /// </summary>
        /// <returns>可用的存储器名称集合 Cacher name array.</returns>
        public static string[] GetCacherNames()
        {
            return _dicCacherNames?.Keys.ToArray();
        }

        /// <summary>
        /// 创建集合器
        /// Create cacher.
        /// </summary>
        /// <param name="cacherName">待创建的集合器名称  Cacher name to be created.</param>
        /// <remarks>
        /// 可以通过调用GetCacherNames()来查看现有的缓存器列表。
        /// You can see cachers list by calling GetCacherNames().
        /// </remarks>
        public CacheFactory(string cacherName)
        {
            Assembly a = Assembly.GetExecutingAssembly();
            if (_dicCacherNames.ContainsKey(cacherName))
            {
                if (_cacher != null)
                {
                    _cacher.Dispose();
                    _cacher = null;
                }
                _cacher = a.CreateInstance(_dicCacherNames[cacherName].FullName) as ICacher;
            }
            else if (_cacher == null)
            {
                Type t = typeof (SimDicCacher);
                _cacher = a.CreateInstance(t.FullName) as ICacher;
            }
        }

        #endregion

        #region ICacher

        public bool Exist(string groupName, string key)
        {
            return _cacher.Exist(groupName, key);
        }

        public DateTime? GetExpireTime(string groupName, string key)
        {
            return _cacher.GetExpireTime(groupName, key);
        }

        public object GetValue(string groupName, string key)
        {
            return _cacher.GetValue(groupName, key);
        }

        public string GetString(string groupName, string key)
        {
            return _cacher.GetString(groupName, key);
        }

        public T GetValue<T>(string groupName, string key)
        {
            return _cacher.GetValue<T>(groupName, key);
        }

        public Dictionary<string, object> GetValues(string groupName, string keyPart)
        {
            return _cacher.GetValues(groupName, keyPart);
        }

        public object PopValue(string groupName, string key)
        {
            return _cacher.PopValue(groupName, key);
        }

        public string PopString(string groupName, string key)
        {
            return _cacher.PopString(groupName, key);
        }

        public T PopValue<T>(string groupName, string key)
        {
            return _cacher.PopValue<T>(groupName, key);
        }

        public TimeSpan DefaultExpireTime
        {
            get { return _cacher.DefaultExpireTime; }
            set { _cacher.DefaultExpireTime = value; }
        }

        public bool SetValue(string groupName, string key, object value, DateTime expiry)
        {
            return _cacher.SetValue(groupName, key, value, expiry);
        }

        public bool SetValue(string groupName, string key, object value)
        {
            return _cacher.SetValue(groupName, key, value);
        }

        public bool Remove(string groupName, string key)
        {
            return _cacher.Remove(groupName, key);
        }

        public bool RemoveGroup(string groupName)
        {
            return _cacher.RemoveGroup(groupName);
        }

        public bool RemoveAll()
        {
            return _cacher.RemoveAll();
        }

        public void Dispose()
        {
        }

        public string CacherName => _cacher.CacherName;

        public int CachedNum()
        {
            return _cacher.CachedNum();
        }

        public int CachedNum(string groupName)
        {
            return _cacher.CachedNum(groupName);
        }

        public void TryTrimAll()
        {
            _cacher.RemoveAll();
        }

        public void DelExpire()
        {
            _cacher.RemoveAll();
        }

        #endregion

        #region ICleaner

        public int Interval
        {
            get { return _interval; }
            set
            {
                _interval = value > LeastInterval ? value : LeastInterval;
                TimerClean.Interval = _interval;
            }
        }

        public void Clean()
        {
            if (_cacher != null) _cacher?.DelExpire();
        }

        #endregion
    }
}
